import warnings
from ctypes import *
import logging
import platform
from can import BusABC, Message
from queue import Queue
from threading import Thread
#先进先出队列
from zlgcan import * 
logger = logging.getLogger(__name__)


class VCI_INIT_CONFIG(Structure):
    _fields_ = [("AccCode", c_int32),
                ("AccMask", c_int32),
                ("Reserved", c_int32),
                ("Filter", c_ubyte),
                ("Timing0", c_ubyte),
                ("Timing1", c_ubyte),
                ("Mode", c_ubyte)]


class VCI_CAN_OBJ(Structure):
    _fields_ = [("ID", c_uint),
                ("TimeStamp", c_int),
                ("TimeFlag", c_byte),
                ("SendType", c_byte),
                ("RemoteFlag", c_byte),
                ("ExternFlag", c_byte),
                ("DataLen", c_byte),
                ("Data", c_ubyte * 8),
                ("Reserved", c_byte * 3)]


VCI_USBCAN2 = 4

STATUS_OK = 0x01
STATUS_ERR = 0x00

TIMING_DICT = {
    5000: (0xBF, 0xFF),
    10000: (0x31, 0x1C),
    20000: (0x18, 0x1C),
    33330: (0x09, 0x6F),
    40000: (0x87, 0xFF),
    50000: (0x09, 0x1C),
    66660: (0x04, 0x6F),
    80000: (0x83, 0xFF),
    83330: (0x03, 0x6F),
    100000: (0x04, 0x1C),
    125000: (0x03, 0x1C),
    200000: (0x81, 0xFA),
    250000: (0x01, 0x1C),
    400000: (0x80, 0xFA),
    500000: (0x00, 0x1C),
    666000: (0x80, 0xB6),
    800000: (0x00, 0x16),
    1000000: (0x00, 0x14),
}


class ZcanBus(BusABC):
    _Device_is_opend = True
    SendMsgQueue = Queue(maxsize = 1000)
    Device_handle = None
    def __init__(
        self,device_type =21,device_index=0, channel_index=0, bitrate=500000, baud=None, Timing0=None, Timing1=None, can_filters=None, **kwargs
    ):
        """

        :param channel: channel number
        :param device: device number
        :param baud: baud rate. Renamed to bitrate in next release.
        :param Timing0: customize the timing register if baudrate is not specified
        :param Timing1:
        :param can_filters: filters for packet
        """
        self._Device_is_opend = False
        self.zcanlib = ZCAN()

        # if platform.system() == "Windows":
            # self.__dll = windll.LoadLibrary("./zlib/zlgcan.dll")
        # else:
            # print("No support now!")
        # if self.__dll == None:

            # print("DLL couldn't be loaded!")

        super(ZcanBus, self).__init__(channel_index, can_filters, **kwargs)

        if isinstance(channel_index, (list, tuple)):
            self.channels = channel_index
        elif isinstance(channel_index, int):
            self.channels = [channel_index]
        else:
            # Assume comma separated string of channels
            self.channels = [int(ch.strip()) for ch in channel_index.split(',')]

        self.device_index = device_index
        self.device_type = device_type

        self.channel_info = "CANalyst-II: device {}, channels {}".format(self.device_index, self.channels)

        if baud is not None:
            warnings.warn('Argument baud will be deprecated in version 4, use bitrate instead',
                          PendingDeprecationWarning)
            bitrate = baud

        if bitrate is not None:
            try:
                Timing0, Timing1 = TIMING_DICT[bitrate]
            except KeyError:
                raise ValueError("Bitrate is not supported")

        if Timing0 is None or Timing1 is None:
            raise ValueError("Timing registers are not set")

        self.init_config = VCI_INIT_CONFIG(0, 0xFFFFFFFF, 0, 1, Timing0, Timing1, 0)

        #如果device 已经打开了， 则只打开channel， 否则先打开device
        if( not self._Device_is_opend):

            self.zcanlib = ZCAN() 
            self.Device_handle = self.can_open(device_index)
            info = self.zcanlib.GetDeviceInf(self.Device_handle)
            logging.info(info)
            self.channel_handle = self.can_start(self.Device_handle, channel_index)
            # self.can_send(self.channel_handle)  #TODO 测试

    def HwSend(self):
        '''
            调用硬件的一次发送多帧，提供传输速度
        '''
        send_max_len_onece = 1000
        while(1):
            queue_len = self.SendMsgQueue.qsize()
            message_len = queue_len if queue_len<send_max_len_onece else send_max_len_onece
            zlgmsgs = (ZCAN_Transmit_Data*message_len)()
            if(message_len > 0):
                for i in range(message_len):
                    msg = self.SendMsgQueue.get()
                    logging.debug(msg)
                    zlgmsgs[i].transmit_type = 0 #0-SingleSend;2-Send Self(Receive Messages)
                    zlgmsgs[i].frame.eff     = 0 #extern frame
                    zlgmsgs[i].frame.rtr =  0#remote frame
                    zlgmsgs[i].frame.can_id  = msg.arbitration_id#0x210
                    zlgmsgs[i].frame.can_dlc = msg.dlc

                    for j in range(zlgmsgs[i].frame.can_dlc):
                        zlgmsgs[i].frame.data[j] = (c_ubyte * 8)(*msg.data)[j]
                ret = self.zcanlib.Transmit(self.channel_handle, zlgmsgs, (message_len))
                logging.debug("发送长度"+str(ret))
            time.sleep(0.10)

    def send(self, msg, timeout=None):
        """

        :param msg: message to send
        :param timeout: timeout is not used here
        :return:
        """
        self.SendMsgQueue.put(msg)
        logging.debug("待发送队列长度"+str(self.SendMsgQueue.qsize()))


        # self.HwSend()

        if msg.channel is not None:
            channel = msg.channel
        elif len(self.channels) == 1:
            channel = self.channels[0]
        else:
            raise ValueError(
                "msg.channel must be set when using multiple channels.")


    def ReciveHw(self, timeout=None):
        '''
        从硬件一次接收多帧
        '''
        pass


    def _recv_internal(self, timeout=None):
        """

        :param timeout: float in seconds
        :return:
        """
        raw_message = VCI_CAN_OBJ()

        timeout = -1 if timeout is None else int(timeout * 1000)

        num = self.zcanlib.GetReceiveNum(self.channel_handle, can_type = ZCAN_TYPE_CAN)
        if num>0:
            logging.log(9,"接收到"+str(num)+"CAN报文")
            raw_messages, ret = self.zcanlib.Receive(self.channel_handle, rcv_num = 1, wait_time = c_int(-1))
        # if CANalystII.VCI_Receive(VCI_USBCAN2, self.device, self.channels[0], byref(raw_message), 1, timeout) <= STATUS_ERR:
            # return None, False
            if ret<= STATUS_ERR:
                return None, False
            else:
                raw_message =raw_messages[0]
                raw_message.frame.rtr=0
                raw_message.frame.eff=0
                msg = Message(
                        timestamp=raw_message.timestamp/1000000 if raw_message.timestamp else 0.0,
                        arbitration_id=raw_message.frame.can_id,
                        # arbitration_id=0x73e,
                        is_extended_id = 0,
                        is_remote_frame=raw_message.frame.rtr,
                        channel=1,
                        dlc=raw_message.frame.can_dlc,
                        data=raw_message.frame.data,
                    )
                # logging.debug(raw_message.timestamp)

                return (
                        msg,
                    False,
                )
        else:
            return None, False

    def flush_tx_buffer(self):
        self.zcanlib.ClearBuffer(self.channel_handle)
        # for channel in self.channels:
            # CANalystII.VCI_ClearBuffer(VCI_USBCAN2, self.device, channel)

    def shutdown(self):
        self.zcanlib.CloseDevice(device_handle=self.Device_handle)
        # CANalystII.VCI_CloseDevice(VCI_USBCAN2, self.device)

    def can_start(self, device_handle, chn):
        zcanlib = self.zcanlib
        chn_init_cfg = ZCAN_CHANNEL_INIT_CONFIG()
        chn_init_cfg.can_type = ZCAN_TYPE_CAN
        #chn_init_cfg.config.canfd.abit_timing = 101166 #1Mbps
        #chn_init_cfg.config.canfd.dbit_timing = 101166 #1Mbps
        chn_init_cfg.config.can.mode        = 0
        chn_init_cfg.config.can.filter      = 0#“0”=标准帧
        chn_init_cfg.config.can.acc_code      = 0
        chn_init_cfg.config.can.acc_mask      = 0xffffffff

        self.can_set_channel(device_handle,chn)
        chn_handle = zcanlib.InitCAN(device_handle, chn, chn_init_cfg)
        print('Dbg:"zlgcan.py" :446 chn_handle=',chn_handle)
        if (chn_handle is None):
            print("初始化失败")
            return None
        res = zcanlib.StartCAN(chn_handle)
        print('Dbg:"zlgcan.py" :451 res=',res)
        if(res == ZCAN_STATUS_OK):
            print("打开启动成功F")
        else:
            print("打开启动失败")

        #启动发送线程

        return chn_handle
    def can_send(self,channel_handle):
        
        print('Dbg:"zlgcan.py" :459 channel_handle=',channel_handle)
        '''
        从指定can通道发送一固定帧报文
        传入参数chn_handle
        '''
        #Send CAN Messages
        transmit_num = 1
        msgs = (ZCAN_Transmit_Data * transmit_num)()
        for i in range(transmit_num):
            msgs[i].transmit_type = 0 #0-SingleSend;2-Send Self(Receive Messages),
            #msgs[i].frame.eff     = 0 #extern frame
            #msgs[i].frame.rtr     = 0 #remote frame
            msgs[i].frame.can_id  = 528#0x210
            msgs[i].frame.can_dlc = 8
            tmp=[1,1,1,1,1,1,1,1]
            for j in range(msgs[i].frame.can_dlc):
                msgs[i].frame.data[j] = tmp[j]

        ret = self.zcanlib.Transmit(self.channel_handle, msgs, transmit_num)
        time.sleep(0.02)
        print("Tranmit Num: %d." % ret)

    def can_open(self,index):
        zcanlib = self.zcanlib
        '''
        打开CAN '''
        handle = zcanlib.OpenDevice(ZCAN_USBCAN_2E_U, index)
        if handle == INVALID_DEVICE_HANDLE :
            print("Open Device failed!")
            exit(0)
        else:
            print("打开",index,'成功')
            #TODO 启动收发线程

            diag_thredt = Thread( target=self.HwSend)  # 通过当前线程开启新的线程去启动事件循环
            diag_thredt.start()

        print("device handle:%d." %(handle))
        info = zcanlib.GetDeviceInf(handle)
        print("Device Information:\n%s" %(info))

        return handle
    def can_set_channel(self,handle,index):
        zcanlib=self.zcanlib
        IProperty= zcanlib.GetIProperty(handle)
        logging.info('Dbg:"zlgcan.py" :466 IProperty='+str(IProperty))
        res=zcanlib.SetValue(IProperty,str(index)+"/baud_rate", "500000")
        if (res!= ZCAN_STATUS_OK):
           print("属性指针为空")
           exit(0)
        zcanlib.ReleaseIProperty(IProperty)
